Previous topicNext topic
Help > TCP and UDP Communications >
An ECHO client and server using TCP

The simplest TCP server application is an Echo Server (RFC 862). It simply listens to port 7, and when it receives a data packet, it returns the data packet back to the client.

Writing a TCP server in PowerBASIC is quite straightforward, but your application must contain a (GUI) window or dialog to receive notification requests from Winsock. It is therefore necessary to either: (1) create a dialog with DDT, or (2) use the Windows API to create a GUI window for the application to receive these notifications. The following function will register a window class, and create a hidden window that can be used by your server.

FUNCTION MakeWindow() AS DWORD

 LOCAL wce         AS WndClassEx

 LOCAL szClassName AS ASCIIZ * 80

 LOCAL hWnd        AS DWORD

 STATIC registered AS LONG

 

 IF ISFALSE registered THEN

   szClassName       = "PBTCPCOMM"

   wce.cbSize        = SIZEOF(wce)

   wce.style         = %NULL

   wce.lpfnWndProc   = CODEPTR(TcpProc)

   wce.cbClsExtra    = 0

   wce.cbWndExtra    = 0

   wce.hInstance     = GetModuleHandle(BYVAL %NULL)

   wce.hIcon         = %NULL

   wce.hCursor       = %NULL

   wce.hbrBackground = %NULL

   wce.lpszMenuName  = %NULL

   wce.lpszClassName = VARPTR(szClassName)

   wce.hIconSm       = %NULL

   RegisterClassEx wce

   registered = %TRUE

 END IF

 

 hWnd = CreateWindow("PBTCPCOMM", "", 0,0,0,0,0, %NULL, %NULL, _

            GetModuleHandle(BYVAL %NULL), BYVAL %NULL)

 ShowWindow hWnd, %SW_HIDE

 

 FUNCTION = hWnd

END FUNCTION

To create a TCP server, your program must first open a socket using the TCP OPEN SERVER statement. Then, when a client contacts your server, this socket will receive the notification. To specify which notifications your code will process, use the TCP NOTIFY statement:

%TCP_ACCEPT = %WM_USER + 4093 ' user-defined message value

...

hServer = FREEFILE

TCP OPEN SERVER PORT 7 AS hServer

TCP NOTIFY hServer, ACCEPT TO hWnd AS %TCP_ACCEPT

TCP NOTIFY tells Winsock that it should send the %TCP_ACCEPT message to the window specified by hWnd. Your callback will then include a message handler for the %TCP_ACCEPT message. The lParam& parameter to your callback will tell you what type of notification was sent:

%TCP_ECHO = %WM_USER + 4094 ' user-defined message value

...

CASE %TCP_ACCEPT

 SELECT CASE LO(WORD, lParam&)

 

   '* An ACCEPT notification was sent

   CASE %FD_ACCEPT

     hEcho = FREEFILE

     TCP ACCEPT hServer AS hEcho

     TCP NOTIFY hEcho, RECV CLOSE TO hWnd AS %TCP_ECHO

 

 .

 . 'other notification code goes here

 .

 END SELECT

Once your code receives the ACCEPT notification, it uses the TCP ACCEPT statement to "close" the socket. A new socket is created for the actual communication with the client. The original socket (hServer) is used strictly to process ACCEPT notifications only. TCP NOTIFY is then used with the new socket handle to process RECV and CLOSE notifications.

When the Echo Client sends its message to your server, a RECV notification will be sent to your window. Your code can then log the incoming message, and send it right back to the client. When the CLOSE notification is received, you can close the socket:

CASE %TCP_ECHO

 SELECT CASE LO(WORD, lParam&)

 

   CASE %FD_READ

     IF hEcho <> %INVALID_SOCKET THEN

       TCP RECV hEcho, 1024, buffer

       TCP SEND hEcho, buffer

       LogEvent $DQ + Buffer + $DQ

     END IF

 

   CASE %FD_CLOSE

     TCP CLOSE hEcho

     hEcho = %INVALID_SOCKET

 

 END SELECT

To connect with the Echo Server, our Client simply needs to open a socket at port 7, send a string, and display the string echoed back from the server.

FUNCTION PBMAIN() AS LONG

 LOCAL hSocket AS LONG

 

 hSocket = FREEFILE

 TCP OPEN PORT 7 AT "" AS hSocket

 IF ERR THEN

   MSGBOX "OPEN Error" + STR$(ERR)

   EXIT FUNCTION

 END IF

 

 IF LEN(COMMAND$) = 0 THEN

   TCP SEND hSocket, "This is a test"

 ELSE

   TCP SEND hSocket, COMMAND$

 END IF

 

 TCP RECV hSocket, 1024, buffer$

 IF ERR THEN

   MSGBOX "RECV Error" + STR$(ERR)

   EXIT FUNCTION

 END IF

 

 MSGBOX buffer$

 

 TCP CLOSE hSocket

END FUNCTION

The complete Echo Server and Echo Client sample can be found in your PB\SAMPLES\INTERNET\TCP folder.

Finally, it should be noted that there is no direct correlation between the number of TCP SEND statements executed, compared to the number of %FD_READ messages received. This is because Winsock may concatenate multiple data packets and issue a lesser number of %FD_READ messages in response. Therefore, it is usually necessary to construct your code so that it continues to read data from the incoming data stream until either the returned string is empty, or an error is detected. For example:

DIM InBuffer AS STRING

...

 CASE %FD_READ

   InBuffer = ""

   IF hEcho = %INVALID_SOCKET THEN EXIT SELECT

 

   DO

     TCP RECV hEcho, 1024, buffer

     IF LEN(buffer) = 0 OR ISTRUE ERR THEN EXIT LOOP

     InBuffer = InBuffer + buffer

     TCP SEND hEcho, buffer

     LogEvent $DQ + Buffer + $DQ

   LOOP

...

 

See Also

TCP and UDP Communications

Simple Mail Transfer Protocol (SMTP)